﻿using AntdUI;
using Azure;
using ImportPlcPointPosition.Models.Table;
using ImportPlcPointPosition.Models.ViewModels;
using ImportPlcPointPosition.Utils;
using Mapster;
using Microsoft.VisualBasic.ApplicationServices;
using MiniExcelLibs;
using NetTaste;
using S7.Net;
using SqlSugar;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ZR.DataCollection2.Core;
using ZR.Model.Models.ProductionControl;
using static System.Runtime.InteropServices.JavaScript.JSType;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace ImportPlcPointPosition.Controls
{
    /// <summary>
    /// 通信点位配置
    /// 左侧显示PLC列表
    ///  选中PLC，加载对应的数据块选项，
    ///  用户可导入或者从数据块加载指定的数据块，对点位数据进行配置
    ///  
    /// </summary>
    public partial class DataPositionPage : BaseUserControl
    {

        protected readonly string Localhost = "127.0.0.1";
        private S7Server server;

        public DataPositionPage()
        {
            InitializeComponent();


            InitTable();

            this.Load += DataPositionPage_Load;

            switch1.CheckedChanged += Switch1_CheckedChanged;

        }

        public override void ReflushPage()
        {
            LoadPlcData();
        }



        private void DataPositionPage_Load(object sender, EventArgs e)
        {
            LoadPlcData();
            _ = StartWatch();
            _ = StartAutoWrite();
        }

        SqlSugarClient db = SqlSugarHelper.MesDB;


        private void InitTable()
        {
            table1.EditMode = TEditMode.None;
            table1.Columns = new ColumnCollection();
            table1.Columns.Add(new Column("WorkstationCode", "工站编号"));


            table1.Bordered = true;
            table1.EnableHeaderResizing = true;
            table1.EmptyHeader = true;
            table1.Gap = 6;
            table1.VisibleHeader = false;

            table1.CellClick += Table1_CellClick;

            table1.SelectIndexChanged += Table1_SelectIndexChanged;


        }

        private void Table1_SelectIndexChanged(object sender, EventArgs e)
        {

            // AntdUI.Message.info(this.FindForm(), $"开始编辑[{table1.SelectedIndex}]", autoClose: 1);

        }

        private void Table1_CellClick(object sender, TableClickEventArgs e)
        {
            var config = e.Record as PlcConfig;
            CurrentPlc = config;
            LoadCheckedPlc(CurrentPlc);
        }



        AntList<PlcConfig> Source = new AntList<PlcConfig>();


        public PlcConfig CurrentPlc = null;
        /// <summary>
        /// 加载数据
        /// </summary>
        private void LoadPlcData()
        {
            Source.Clear();
            var db = SqlSugarHelper.MesDB;
            var lst = db.Queryable<PlcConfig>().ToList();
            foreach (var item in lst)
            {
                Source.Add(item);
            }
            this.table1.Binding(Source);
            if (Source.Count > 0 && table1.SelectedIndex < 0)
            {
                table1.SelectedIndex = 1;
                CurrentPlc = Source[0];
                LoadCheckedPlc(CurrentPlc);
            }


        }

        private void LoadCheckedPlc(PlcConfig config)
        {


            LoadPlcBockDbs(config);
        }

        List<BlockDbTableControl> blockDbTables = new List<BlockDbTableControl>();
        /// <summary>
        /// 调用BlockDbTable IsWatch变化
        /// </summary>
        private void ChangePointTableColumnVisible()
        {
            foreach (var item in blockDbTables)
            {
                item.IsWatch = switch1.Checked;
                item.ChangePointTableColumnVisible();
            }
            IsLocalServer.Enabled = !switch1.Checked;
        }




        /// <summary>
        /// 初始化下拉控件的数据块配置
        /// </summary>
        /// <param name="config"></param>
        private void LoadPlcBockDbs(PlcConfig config)
        {

            var items = db.Queryable<PlcBlockDb>().InnerJoin<PlcConfig>((a, b) => a.PlcId == b.Id)
                .Select((a, b) => new PlcBlockDb() { WorkstationCode = b.WorkstationCode }, true)
                .Where(a => a.PlcId == config.Id)
                .OrderBy(a => a.BlockDb)
                .ToList();
            blockDbTables.Clear();
            tabs1.Pages.Clear();
            if (items.Count == 0)
            {
                MessageInfo("请先设置DB块再打开当前页面");
                return;
            }
            int index = 0;
            foreach (var blockDb in items)
            {
                string desc = blockDb.WriteFrom switch
                {
                    1 => "PC",
                    2 => "PLC",
                    3 => "混合",
                    _ => ""
                };
                var tabPage1 = new AntdUI.TabPage() { Text = $"{desc}_DB{blockDb.BlockDb}" };
                var dbTable = new BlockDbTableControl();
                dbTable.BlockDb = blockDb;
                dbTable.CurrentPlc = CurrentPlc;
                dbTable.Width = 100;
                dbTable.Height = 100;
                dbTable.Name = $"page{blockDb.Id}";
                dbTable.Location = new Point(10, 10);
                dbTable.Parent = tabPage1;
                dbTable.Dock = DockStyle.Fill;
                tabs1.Pages.Add(tabPage1);

                tabPage1.Controls.Add(dbTable);
                blockDbTables.Add(dbTable);

                index++;
            }



            UpdatePageHeader();

        }

        private void UpdatePageHeader(int selectIndex = 0)
        {
            var config = CurrentPlc;
            var dbcontrol = tabs1.Pages[selectIndex].Controls[0] as BlockDbTableControl;

            pageHeader1.Text = $"点位配置 - [{config.WorkstationCode}] - [{config.PlcAddress}] - [{dbcontrol?.BlockDb?.Remark}]";
        }


        private void Switch1_CheckedChanged(object sender, BoolEventArgs e)
        {
            if (e.Value)
            {
                AntdUI.Spin.open(this, new AntdUI.Spin.Config()
                {
                    Back = Color.FromArgb(220, 147, 181, 207),
                    Color = Style.Db.Primary,//转圈颜色

                    Radius = 6,
                    Fore = Color.Black,//字体颜色
                    Font = new Font("Microsoft YaHei UI", 14f),//字体可以控制进度圈的大小
                }, (config) =>
                {
                    try
                    {
                        config.Text = "开启监视服务...";
                        config.Value = 0.4f;
                        Thread.Sleep(200);

                        //启动模拟仿真
                        StartServer();
                        //耗时代码，处理数据
                        bool connect = StartListen(out string message);

                        switch1.Checked = connect;
                        config.Value = 1.0f;
                        if (!connect)
                            MessageError("连接失败");
                        else
                            MessageInfo("连接成功");
                    }
                    catch (Exception ex)
                    {
                        config.Value = 1.0f;
                        MessageError("连接异常:" + ex.Message);
                    }

                }, () =>
                {
                    //结束时执行的代码
                    ChangePointTableColumnVisible();
                });
            }
            else
            {
                ClosePlc();
                ChangePointTableColumnVisible();
            }
        }
        S7Server.USrvEvent Event;
        private void StartServer()
        {
            server = new S7Server();
            var flag = server.StartTo(Localhost);


            //模拟数据需要制造多个DB块

            foreach (var p in blockDbTables)
            {
                var Buffer = new byte[p.BlockDb.BlockLength];
                server.RegisterArea(S7Server.SrvAreaDB, p.BlockDb.BlockDb, ref Buffer, Buffer.Length);
            }


        }

        private void CloseServer()
        {
            try
            {
                if (server != null)
                {
                    server.Stop();
                    server = null;
                }
            }
            catch (Exception)
            {
            }
        }

        public void ClosePlc()
        {
            CloseServer();
            lock (App.lockClient)
            {
                if (App.PlcClient != null)
                {
                    App.PlcClient.Close();
                    App.PlcClient = null;
                    MessageInfo("连接已关闭");
                }
                try
                {
                    if (this.IsHandleCreated)
                        this.Invoke(() =>
                        {
                            table1.Enabled = true;
                        });
                }
                catch (Exception)
                {
                }

            }
        }

        private bool StartListen(out string message)
        {
            message = "";


            if (CurrentPlc == null)
            {
                switch1.Checked = false;
                return false;
            }

            //var dbPanel = tabs1.SelectedTab.Controls[0] as BlockDbTable;
            //var PointDatas = dbPanel.PointDatas;
            //var plcdb = dbPanel.BlockDb;


            if (blockDbTables.Count == 0)
            {
                switch1.Checked = false;
                return false;
            }

            //使用全局plcClient对象；
            bool IsConnect = false;
            lock (App.lockClient)
            {
                if (App.PlcClient != null)
                {
                    App.PlcClient.Close();
                    App.PlcClient = null;
                }
                try
                {
                    string connectIp = IsLocalServer.Checked ? Localhost : CurrentPlc.PlcAddress;
                    App.PlcClient = new PLC.PlcClient(connectIp);
                    IsConnect = App.PlcClient.Connect();
                }
                catch (Exception)
                {
                    App.PlcClient = null;
                    throw;
                }
                if (!IsConnect)
                    App.PlcClient = null;
            }




            this.Invoke(() =>
            {
                table1.Enabled = !IsConnect;
            });
            return IsConnect;

        }
        /// <summary>
        /// 监视数据
        /// </summary>
        /// <returns></returns>
        private async Task StartWatch()
        {
            try
            {
                while (this.IsHandleCreated) //
                {
                    try
                    {
                        if (!this.switch1.Checked && App.PlcClient != null)
                            App.PlcClient.Decoder = null;//重置解析器
                        await WatchPlcData();
                    }
                    catch (Exception)
                    {

                    }

                    await Task.Delay(100); // 每100毫秒读取一次
                }
            }
            catch (Exception ex)
            {

            }


        }

        private async Task StartAutoWrite()
        {
            try
            {
                while (this.IsHandleCreated) //
                {

                    await AutoWrite();
                    await Task.Delay(1000); // 每100毫秒读取一次
                }
            }
            catch (Exception ex)
            {

            }


        }


        public async Task AutoWrite()
        {
            try
            {
                if (App.PlcClient == null || !App.PlcClient.plcSession.IsConnected)
                    return;

                List<Task> tasks = new List<Task>();
                foreach (var page in tabs1.Pages)
                {
                    var dbPanel = page.Controls[0] as BlockDbTableControl;
                    var task = Task.Run(() =>
                    {
                        CheckWriteTable(dbPanel.PointDatas.ToList());
                    });
                    tasks.Add(task);

                }

                await Task.WhenAll(tasks);



            }
            catch (Exception)
            {

            }


        }

        private static void CheckWriteTable(List<ImportData> datas)
        {
            foreach (var item in datas)
            {
                WriteAutoVal(item);
            }
        }

        private static void WriteAutoVal(ImportData item)
        {
            if (item.AutoWrite)
            {
                var items = item.WriteValue.Split(',');
                item.LastWriteIndex++;
                if (item.LastWriteIndex >= items.Length)
                {
                    item.LastWriteIndex = 0;
                }
                var writeModel = item.Adapt<CksPlcDataPosition>();
                string writeObject = items[item.LastWriteIndex];
                //如果是字符串 ，自动生成一个编码 CKS
                if (writeModel.ParseType.Equals("string"))
                {
                    var dt = DateTime.Now;
                    writeObject = $"CKS{dt.ToString("yyyyMMddHHmmss")}";
                }
                App.PlcClient.SetValue(writeModel, writeObject);
            }
            if (item.SubItems != null && item.SubItems.Length > 0)
            {
                CheckWriteTable(item.SubItems.ToList());
            }

        }

        int LastWatchDB = 0;

        /// <summary>
        /// 监听PLC数据、遍历数据块
        /// </summary>
        /// <returns></returns>
        private async Task WatchPlcData()
        {
            await Task.Yield();
            if (App.PlcClient == null || !App.PlcClient.plcSession.IsConnected)
                return;
            if (CurrentPlc == null)
                return;
            if (blockDbTables.Count == 0)
                return;
            //只监视当前显示的db块
            int exceptiondb = 0;
            try
            {


                List<Task> tasks = new List<Task>();
                foreach (var page in tabs1.SelectedTab.Controls)
                {
                    var task = Task.Run(async () =>
                    {
                        var dbPanel = page as BlockDbTableControl;
                        var PointDatas = PointDataToList(dbPanel.PointDatas);
                        var plcdb = dbPanel.BlockDb;
                        var plcSession = App.PlcClient.plcSession;
                        exceptiondb = plcdb.BlockDb;
                        var blockBytes = await plcSession.ReadBytesAsync(DataType.DataBlock, plcdb.BlockDb, 0, plcdb.BlockLength);
                        var points = PointDatas.Adapt<List<CksPlcDataPosition>>();
                        SieminDecoder decoder = App.PlcClient.Decoder;
                        bool isFirst = false;
                        if (decoder == null || LastWatchDB != plcdb.BlockDb)
                        {
                            decoder = App.PlcClient.Decoder = new SieminDecoder(points);
                            isFirst = true;
                        }


                        LastWatchDB = plcdb.BlockDb;
                        var changeValues = decoder.Decode(blockBytes).ToList();
                        if (!isFirst)
                            changeValues = changeValues.FindAll(a => a.IsChange);


                        foreach (var item in changeValues)
                        {
                            var one = PointDatas.Where(a => a.Id == item.Position.Id).First();
                            if (one != null)
                            {

                                //判断是否是数组 ， 如果是数组，更新数组的监视值
                                if (SieminDecoder.IsArrayType(one.ParseType, out int len, out string tt))
                                {
                                    if (one.SubItems != null && tt.IsS7Integer())
                                    {
                                        var ArrayVals = item.Value.Adapt<List<int>>();
                                        for (int i = 0; i < one.SubItems.Count(); i++)
                                        {
                                            one.SubItems[i].WatchValue = ArrayVals[i].ToString();
                                        }
                                    }
                                    else if (one.SubItems != null && tt.ToLower().Equals("real"))
                                    {
                                        var ArrayVals = item.Value.Adapt<List<decimal>>();
                                        for (int i = 0; i < one.SubItems.Count(); i++)
                                        {
                                            one.SubItems[i].WatchValue = ArrayVals[i].ObjToDecimal().ToString();
                                        }
                                    }
                                    else if (one.SubItems != null && (item.Value is Array || item.Value is System.Collections.IList))
                                    {
                                        var ArrayVals = item.Value.Adapt<List<string>>();
                                        for (int i = 0; i < one.SubItems.Count(); i++)
                                        {
                                            one.SubItems[i].WatchValue = ArrayVals[i];
                                        }
                                    }

                                }
                                else
                                {
                                    one.WatchValue = item.Value.ToString();
                                }
                            }



                        }
                    });
                    tasks.Add(task);

                }

                await Task.WhenAll(tasks);



            }
            catch (Exception ex)
            {
                if (ex is S7.Net.PlcException)
                {
                    var s7ex = ex as S7.Net.PlcException;
                    if (s7ex.ErrorCode == ErrorCode.ConnectionError)
                        return;
                }
                MessageBoxWarn("请尽快修复", $"读取出错了 {exceptiondb}");
                switch1.Checked = false;
                //Received error from PLC: Address out of range :这个错是DB块和配置的DB块不匹配，找PLC重新要一份DB文件
            }
        }





        private void btnSaveAll_Click(object sender, EventArgs e)
        {

        }

        private void btnImportExcel_Click(object sender, EventArgs e)
        {

        }

        private void tabs1_SelectedIndexChanged(object sender, IntEventArgs e)
        {
            UpdatePageHeader(e.Value);
        }
    }
}
